﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Tilver.Coder.Core;
using Tilver.Coder.Utility;

namespace Tilver.Coder.Engine
{
    internal class TableMOEngine : MOEngine<CoderTable>, ITableEngineMO
    {

        #region ITableEngine 成员

        public CoderTable Table
        {
            get
            {
                return base.MO;
            }
            set
            {
                base.MO = value;
            }
        }

        #endregion

        protected override void ResponseSelects(StringBuilder response, int tabs)
        {
            ResponseSelectPKs(response, tabs);//输出主键操作方法
            response.AppendLine();
            //ResponseSelectUKs(response, tabs);//输出唯一键方法
            //response.AppendLine();
            //ResponseSelectFKs(response, tabs);//输出外键方法
            //response.AppendLine();
            base.ResponseSelects(response, tabs);
        }

        #region Select

        private void ResponseSelectPKs(StringBuilder response, int tabs)
        {
            foreach (var index in Table.Indexes.Where(w => w.IsPrimaryKey))
            {
                ResponseSelectPkAndUnique(response, tabs, index);
            }
        }

        private void ResponseSelectUKs(StringBuilder response, int tabs)
        {
            foreach (var index in Table.Indexes.Where(w => (!w.IsPrimaryKey) && w.IsUnique))
            {
                ResponseSelectPkAndUnique(response, tabs, index);
            }
        }

        protected void ResponseSelectPkAndUnique(StringBuilder response, int tabs, CoderIndex index)
        {
            string summaryName = string.Format("按{0}查询", index.IsPrimaryKey ? "主键" : "唯一键");
            string functionName = index.IsPrimaryKey ? "PK" : ExtensionUtil.GetDataObjectsInfo(tabs, index.MemberColumns, ExtensionUtil.IndexType.FunctionName);
            string paramDesc = ExtensionUtil.GetDataObjectsInfo(tabs, index.MemberColumns, ExtensionUtil.IndexType.ParamSummary);
            string paramList = ExtensionUtil.GetDataObjectsInfo(tabs, index.MemberColumns, ExtensionUtil.IndexType.ParamList);
            string inputParam = ExtensionUtil.GetDataObjectsInfo(tabs, index.MemberColumns, ExtensionUtil.IndexType.InputParam);

            string mainMethod = string.Format("public {0} SelectBy{1}({2}, TransactionManager tm = null)", Table.EOName, functionName, paramList);
            var boMethodInfo = Table.MOInfo.Methods.Find(w => w.Method == mainMethod);
            if (boMethodInfo == null)
            {
                boMethodInfo = new CoderMOMethodInfo();
                boMethodInfo.CommandType = MOMethodCommandTypeEnum.Select;
                boMethodInfo.MethodType = index.IsPrimaryKey ? MOMethodTypeEnum.FK : MOMethodTypeEnum.UK;
                boMethodInfo.Method = mainMethod;
                boMethodInfo.IsBuild = true;
                Table.MOInfo.Methods.Add(boMethodInfo);
            }

            if (boMethodInfo.IsBuild)
            {
                response.AppendLine(ExtensionUtil.GetCoderSummary(tabs, summaryName));
                response.AppendLine(paramDesc);
                response.AppendLine(ExtensionUtil.GetCoderTMSummary(tabs));
                response.AppendLine(ExtensionUtil.GetCoderReturnSummary(tabs, string.Empty));
                response.AppendTabLine(tabs, mainMethod);
                response.AppendTabLine(tabs, "{");
                response.AppendTabLine(tabs + 1, "var sql = new StringBuilder();");

                string select = "SELECT ";
                string from = "FROM ";
                string where = "WHERE ";
                foreach (var column in Table.Columns)
                {
                    select += DBTextStyle.GetColumnName(column.Name) + ", ";
                }
                from += DBTextStyle.GetTableFullName(Table.Owner, Table.Name) + " ";
                var sbPara = new StringBuilder();
                foreach (var column in index.MemberColumns)
                {
                    string sqlColumnName = DBTextStyle.GetColumnName(column.Name);
                    string sqlParamName = DBTextStyle.GetParameterName(column.Name);
                    where += string.Format("{0}={1} AND ", sqlColumnName, sqlParamName);
                    sbPara.AppendFormatLine(tabs + 5, ".AddInParameter(\"{0}\", {1}, DbType.{2}, {3})",
                                            sqlParamName,
                                            column.CodeName.ToCoderParameterName(),
                                            column.DbType,
                                            column.Size);
                }
                response.AppendFormatLine(tabs + 1, "sql.AppendFormat(@\"{0} {1} {2} \");",
                    select.TrimEnd(' ', ','),
                    from.TrimEnd(' ', ','),
                    where.TrimString(string.Empty, " AND ")
                    );
                response.AppendTabLine(tabs + 1, "return AdoBase(sql.ToString())");
                response.Append(sbPara);
                response.AppendFormatLine(tabs + 5, ".ExecuteReader(tm).MapToSingle(new {0}().GetMapper());", Table.EOName);
                response.AppendTabLine(tabs, "}");
                response.AppendLine();
            }
        }

        private void ResponseSelectFKs(StringBuilder response, int tabs)
        {
            foreach (var key in Table.ForeignKeys)
            {
                if (CheckIsOnlyFK(key)) continue;
                //ResponseSelectFKAndColumn(response, tabs, key.ForeignColumns, true);
            }
        }

        //是外键但不是唯一外键
        protected bool CheckIsOnlyFK(CoderTableKey key)
        {
            bool ret = false;
            int equalNum = 0;
            foreach (var idx in Table.Indexes)
            {
                if (ret) break;
                if (idx.IsUnique && !idx.IsPrimaryKey)
                {
                    if (idx.MemberColumnNames.Count == key.ForeignColumnNames.Count)
                    {
                        equalNum = 0;
                        foreach (string uniqueColumn in idx.MemberColumnNames)
                        {
                            if (key.ForeignColumnNames.Contains(uniqueColumn))
                                equalNum++;
                        }
                        if (equalNum == idx.MemberColumnNames.Count)
                            ret = true;
                    }
                }
            }
            return ret;
        }

        #endregion

        #region Insert
        /// <summary>
        /// 增加
        /// </summary>
        /// <param name="response"></param>
        /// <param name="tabs"></param>
        protected override void ResponseInsert(StringBuilder response, int tabs)
        {
            base.ResponseInsert(response, tabs);
            ResponseInsertContent(response, tabs);
        }

        private void ResponseInsertContent(StringBuilder response, int tabs)
        {
            string mainMethod = string.Format("public int Insert({0} item, TransactionManager tm = null)", Table.EOName);
            var boMethodInfo = Table.MOInfo.Methods.Find(w => w.Method == mainMethod);
            if (boMethodInfo == null)
            {
                boMethodInfo = new CoderMOMethodInfo();
                boMethodInfo.CommandType = MOMethodCommandTypeEnum.Insert;
                boMethodInfo.MethodType = MOMethodTypeEnum.Other;
                boMethodInfo.Method = mainMethod;
                boMethodInfo.IsBuild = true;
                Table.MOInfo.Methods.Add(boMethodInfo);
            }

            if (boMethodInfo.IsBuild)
            {
                response.AppendLine(ExtensionUtil.GetCoderSummary(tabs, "插入数据，有自增列时，item中自增字段为新增值，否则，返回影响的行数"));
                response.AppendLine(ExtensionUtil.GetCoderTMSummary(tabs));
                response.AppendLine(ExtensionUtil.GetCoderParamSummary(tabs, "item", "要插入的实体对象"));
                response.AppendLine(ExtensionUtil.GetCoderReturnSummary(tabs, string.Empty));
                response.AppendTabLine(tabs, mainMethod);
                response.AppendTabLine(tabs, "{");
                var fieldList = string.Empty;
                var paraList = string.Empty;
                var hasIdentity = false;//是否有自增字段
                CoderColumn identityColumn = null;
                foreach (var column in Table.Columns)
                {
                    if (column.IsIdentity)
                    {
                        hasIdentity = true;
                        identityColumn = column;
                        continue;
                    }
                    if (column.NativeType == "timestamp") continue;
                    fieldList += string.Format("{0}, ", DBTextStyle.GetColumnName(column.Name));
                    var paraName = DBTextStyle.GetParameterName(column.Name);
                    paraList += paraName + ", ";
                }

                fieldList = fieldList.Trim(',', ' ');
                paraList = paraList.Trim(',', ' ');
                //var identitySql = hasIdentity ? "SELECT SCOPE_IDENTITY();" : string.Empty;
                var identitySql = hasIdentity ? DBTextStyle.GetInsertIdentitySql() : string.Empty;
                response.AppendFormatLine(tabs + 1, "const string sql = @\"INSERT INTO {0} ({1}) VALUES ({2}) {3}\";",
                    DBTextStyle.GetTableFullName(Table.Owner, Table.Name),
                    fieldList,
                    paraList,
                    identitySql);
                string para;
                GetParaInfo(tabs + 10, Table, false, out para);
                //Oracle不支持自增ID，要通过SEQUENCE自增序列来实现
                if (hasIdentity && DBTextStyle.DataProviderType != DataProviderTypeEnum.Oracle)
                {
                    response.AppendFormatLine(tabs + 1,
                        "item.{0} = {1}.Parse(AdoBase(sql)",
                        identityColumn.CodeName.ToCoderPropertyName(),
                        identityColumn.CSharpType);
                    response.Append(para);
                    response.AppendTabLine(tabs + 10, ".ExecuteScalar(tm).ToString());");
                    response.AppendTabLine(tabs + 1, "return 1;");
                }
                else
                {
                    response.AppendTabLine(tabs + 1, "return AdoBase(sql)");
                    response.Append(para);
                    response.AppendTabLine(tabs + 10, ".ExecuteNonQuery(tm);");
                }
                response.AppendTabLine(tabs, "}");
            }
        }

        private void ResponseInsertMutils(StringBuilder response, int tabs)
        {
            string mainMethod = string.Format("public virtual int Insert(TransactionManager tm , IEnumerable<{0}> items)", Table.EOName);
            var boMethodInfo = Table.MOInfo.Methods.Find(w => w.Method == mainMethod);

            if (boMethodInfo == null)
            {
                boMethodInfo = new CoderMOMethodInfo();

                boMethodInfo.CommandType = MOMethodCommandTypeEnum.Insert;
                boMethodInfo.MethodType = MOMethodTypeEnum.Other;
                boMethodInfo.Method = mainMethod;
                boMethodInfo.IsBuild = true;
                Table.MOInfo.Methods.Add(boMethodInfo);
            }

            if (boMethodInfo.IsBuild)
            {
                response.AppendLine(ExtensionUtil.GetCoderSummary(tabs, "插入数据"));
                response.AppendLine(ExtensionUtil.GetCoderParamSummary(tabs, "items", "要插入的实体对象集合"));
                response.AppendLine(ExtensionUtil.GetCoderReturnSummary(tabs, string.Empty));
                response.AppendFormatLine(tabs, "public virtual int Insert(IEnumerable<{0}> items)", Table.EOName);
                response.AppendTabLine(tabs, "{");
                response.AppendTabLine(tabs + 1, "return Insert(null, items);");
                response.AppendTabLine(tabs, "}");
                response.AppendLine();
                response.AppendLine(ExtensionUtil.GetCoderSummary(tabs, "插入数据"));
                response.AppendLine(ExtensionUtil.GetCoderTMSummary(tabs));
                response.AppendLine(ExtensionUtil.GetCoderParamSummary(tabs, "items", "要插入的实体对象集合"));
                response.AppendLine(ExtensionUtil.GetCoderReturnSummary(tabs, string.Empty));
                response.AppendTabLine(tabs, mainMethod);
                response.AppendTabLine(tabs, "{");
                ResponseInsertContentMutils(response, tabs + 1);
                response.AppendTabLine(tabs, "}");
            }
        }

        private void ResponseInsertContentMutils(StringBuilder response, int tabs)
        {
            string para = string.Empty;
            string paraValue = string.Empty;
            string fieldList = string.Empty;
            string paraList = string.Empty;
            bool hasIdentity = false;
            CoderColumn identityColumn = null;

            for (int i = 0; i < Table.Columns.Count; i++)
            {
                var column = Table.Columns[i];
                if (column.IsIdentity)
                {
                    hasIdentity = true;
                    identityColumn = column;
                    continue;
                }
                if (column.NativeType == "timestamp") continue;
                fieldList += string.Format("{0}, ", DBTextStyle.GetColumnName(column.Name));
                string paraName = DBTextStyle.GetParameterName(column.Name);
                paraList += paraName + ", ";
            }

            fieldList = fieldList.Trim(',', ' ');
            paraList = paraList.Trim(',', ' ');
            //string identitySql = hasIdentity ? string.Format("select IDENT_CURRENT(N'{0}')", GetTableFullName()) : string.Empty;
            string identitySql = hasIdentity ? "select SCOPE_IDENTITY()" : string.Empty;

            response.AppendTabLine(tabs, "int ret = 0;");
            response.AppendFormatLine(tabs, "const string sql = @\"insert into {0} ({1}) values ({2}) {3}\";", DBTextStyle.GetTableFullName(Table.Owner, Table.Name), fieldList, paraList, identitySql);

            GetParaInfo(tabs + 9, Table, true, out para);
            response.AppendLine();
            response.AppendFormatLine(tabs, "foreach ({0} item in items)", Table.EOName);
            response.AppendTabLine(tabs, "{");


            if (hasIdentity)
            {
                response.AppendFormatLine(tabs + 1,
                    "item.{0} = {1}.Parse(Database.CreateSqlDao(sql)",
                    identityColumn.CodeName.ToCoderPropertyName(),
                    identityColumn.CSharpType);
                response.Append(para);
                response.AppendTabLine(tabs + 10, ".ExecuteScalar(tm).ToString());");
            }
            else
            {
                response.AppendTabLine(tabs + 1, "Database.CreateSqlDao(sql)");
                response.Append(para);
                response.AppendTabLine(tabs + 10, ".ExecuteNonQuery(tm);");
            }
            response.AppendTabLine(tabs + 1, "++ret;");
            response.AppendTabLine(tabs, "}");
            response.AppendTabLine(tabs, "return ret;");
        }

        #endregion

        #region Update
        /// <summary>
        /// 更新
        /// </summary>
        /// <param name="response"></param>
        /// <param name="tabs"></param>
        protected override void ResponseUpdate(StringBuilder response, int tabs)
        {
            base.ResponseUpdate(response, tabs);
            ResponseUpdateContent(response, tabs);
            ResponseUpdateColumns(response, tabs);
            ResponseUpdateCustom(response, tabs);
        }
        #region 按条件更新
        /// <summary>
        /// 自定义方法
        /// </summary>
        /// <param name="response"></param>
        /// <param name="tabs"></param>
        protected virtual void ResponseUpdateCustom(StringBuilder response, int tabs)
        {
            /*
            /// <summary>
            /// 按自定义条件更新数据
            /// </summary>
            /// <param name="set">更新列</param>
            /// <param name="where">更新条件</param>
            /// <param name="paras">参数集合</param>
            /// <param name="tm">事务管理对象</param>
            /// <returns></returns>
            public int Update(string set, string where, IList<DbParameter> paras, TransactionManager tm = null)
            {
                if (string.IsNullOrEmpty(set)) throw new ArgumentNullException("set");
                var sql = string.Format(@"UPDATE [dbo].[CustomerInfo] SET {0} {1}", set, (string.IsNullOrEmpty(where) ? string.Empty : " WHERE " + where));
                return AdoBase(sql)
                                .AddParameters(paras)
                                .ExecuteNonQuery(tm);
            }
             */
            var mainMethod = string.Format("public int Update(string set, string where, IEnumerable<DbParameter> paras, TransactionManager tm = null)");
            var boMethodInfo = MO.MOInfo.Methods.Find(w => w.Method == mainMethod);
            if (boMethodInfo == null)
            {
                boMethodInfo = new CoderMOMethodInfo();
                boMethodInfo.CommandType = MOMethodCommandTypeEnum.Update;
                boMethodInfo.MethodType = MOMethodTypeEnum.Other;
                boMethodInfo.Method = mainMethod;
                boMethodInfo.IsBuild = true;
                MO.MOInfo.Methods.Add(boMethodInfo);
            }
            if (boMethodInfo.IsBuild)
            {
                response.AppendLine(ExtensionUtil.GetCoderSummary(tabs, "按自定义条件更新数据"));
                response.AppendLine(ExtensionUtil.GetCoderParamSummary(tabs, "set", "更新列"));
                response.AppendLine(ExtensionUtil.GetCoderParamSummary(tabs, "where", "更新条件"));
                response.AppendLine(ExtensionUtil.GetCoderParamSummary(tabs, "paras", "参数集合"));
                response.AppendLine(ExtensionUtil.GetCoderTMSummary(tabs));
                response.AppendLine(ExtensionUtil.GetCoderReturnSummary(tabs, string.Empty));
                response.AppendTabLine(tabs, mainMethod);
                response.AppendTabLine(tabs, "{");
                response.AppendTabLine(tabs + 1, "if (string.IsNullOrEmpty(set)) throw new ArgumentNullException(\"set\");");
                response.AppendFormatLine(tabs + 1, "var sql = string.Format(@\"UPDATE {0} SET {{0}} {{1}}\", set, (string.IsNullOrEmpty(where) ? string.Empty : \" WHERE \" + where));",
                    DBTextStyle.GetTableFullName(MO.Owner, MO.Name));
                response.AppendTabLine(tabs + 1, "return AdoBase(sql)");
                response.AppendTabLine(tabs + 5, ".AddParameters(paras)");
                response.AppendTabLine(tabs + 5, ".ExecuteNonQuery(tm);");
                response.AppendTabLine(tabs, "}");
            }
        }
        #endregion
        #region 按主键字段更新
        /// <summary>
        /// 按主键字段更新所有字段信息
        /// </summary>
        /// <param name="response"></param>
        /// <param name="tabs"></param>
        private void ResponseUpdateContent(StringBuilder response, int tabs)
        {
            if (!Table.HasPrimaryKey) return;//没有主键时不输出此方法

            var mainMethod = string.Format("public int Update({0} item, TransactionManager tm = null)", Table.EOName);
            var boMethodInfo = Table.MOInfo.Methods.Find(w => w.Method == mainMethod);
            if (boMethodInfo == null)
            {
                boMethodInfo = new CoderMOMethodInfo();
                boMethodInfo.CommandType = MOMethodCommandTypeEnum.Update;
                boMethodInfo.MethodType = MOMethodTypeEnum.Other;
                boMethodInfo.Method = mainMethod;
                boMethodInfo.IsBuild = true;
                Table.MOInfo.Methods.Add(boMethodInfo);
            }
            if (boMethodInfo.IsBuild)
            {
                response.AppendLine(ExtensionUtil.GetCoderSummary(tabs, "更新数据"));
                response.AppendLine(ExtensionUtil.GetCoderParamSummary(tabs, "item", "要更新的实体对象"));
                response.AppendLine(ExtensionUtil.GetCoderParamSummary(tabs, "tm", "事务管理对象"));
                response.AppendLine(ExtensionUtil.GetCoderReturnSummary(tabs, string.Empty));
                response.AppendTabLine(tabs, mainMethod);
                response.AppendTabLine(tabs, "{");
                foreach (var column in Table.Columns)
                {
                    if (!column.AllowDBNull && !column.IsValueType)
                    {
                        response.AppendFormatLine(tabs, "if (item.{0} == null) throw new ArgumentNullException(\"属性{0}不能为null.\");", column.CodeName.ToCoderPropertyName());
                    }
                }
                var fieldList = string.Empty;
                var where = string.Empty;
                foreach (var column in Table.Columns)
                {
                    if (column.IsIdentity || (column.NativeType == "timestamp"))
                        continue;
                    var paraName = DBTextStyle.GetParameterName(column.Name);
                    fieldList += string.Format("{0}={1}, ", DBTextStyle.GetColumnName(column.Name), paraName);
                }
                fieldList = fieldList.Trim(',', ' ');
                for (var i = 0; i < Table.PrimaryKey.MemberColumnNames.Count; i++)
                {
                    var columnName = Table.PrimaryKey.MemberColumnNames[i];
                    var paraName = GetOriginalParamName(columnName);
                    where += string.Format("{0}={1}", DBTextStyle.GetColumnName(columnName), paraName) +
                        ((i == (Table.PrimaryKey.MemberColumnNames.Count - 1)) ? string.Empty : " AND ");
                }
                response.AppendFormatLine(tabs + 1, "const string sql = @\"UPDATE {0} SET {1} WHERE {2}\";",
                    DBTextStyle.GetTableFullName(Table.Owner, Table.Name),
                    fieldList,
                    where);
                response.AppendTabLine(tabs + 1, "return AdoBase(sql)");
                string para;
                GetPrimayColumnParaInfo(tabs + 5, Table, false, out para);
                response.Append(para);
                GetParaInfo(tabs + 5, Table, false, out para);
                response.Append(para);
                response.AppendTabLine(tabs + 5, ".ExecuteNonQuery(tm);");
                response.AppendTabLine(tabs, "}");
            }

        }
        #endregion
        #region 更新多个
        private void ResponseUpdateMutils(StringBuilder response, int tabs)
        {
            string mainMethod = string.Format("public virtual int Update(TransactionManager tm, IEnumerable<{0}> items)", Table.EOName);
            var boMethodInfo = Table.MOInfo.Methods.Find(w => w.Method == mainMethod);

            if (boMethodInfo == null)
            {
                boMethodInfo = new CoderMOMethodInfo();

                boMethodInfo.CommandType = MOMethodCommandTypeEnum.Update;
                boMethodInfo.MethodType = MOMethodTypeEnum.Other;
                boMethodInfo.Method = mainMethod;
                boMethodInfo.IsBuild = true;
                Table.MOInfo.Methods.Add(boMethodInfo);
            }

            if (boMethodInfo.IsBuild)
            {
                response.AppendLine(ExtensionUtil.GetCoderSummary(tabs, "更新数据"));
                response.AppendLine(ExtensionUtil.GetCoderParamSummary(tabs, "items", "要更新的实体对象集合"));
                response.AppendLine(ExtensionUtil.GetCoderReturnSummary(tabs, string.Empty));
                response.AppendFormatLine(tabs, "public virtual int Update(IEnumerable<{0}> items)", Table.EOName);
                response.AppendTabLine(tabs, "{");
                response.AppendTabLine(tabs + 1, "return Update(null, items);");
                response.AppendTabLine(tabs, "}");

                response.AppendLine();

                response.AppendLine(ExtensionUtil.GetCoderSummary(tabs, "更新数据"));
                response.AppendLine(ExtensionUtil.GetCoderTMSummary(tabs));
                response.AppendLine(ExtensionUtil.GetCoderParamSummary(tabs, "items", "要更新的实体对象集合"));
                response.AppendLine(ExtensionUtil.GetCoderReturnSummary(tabs, string.Empty));
                response.AppendTabLine(tabs, mainMethod);
                response.AppendTabLine(tabs, "{");
                ResponseUpdateContentMutils(response, tabs + 1);
                response.AppendTabLine(tabs, "}");
            }
        }

        private void ResponseUpdateContentMutils(StringBuilder response, int tabs)
        {
            bool valueChecked = false;

            for (int i = 0; i < Table.Columns.Count; i++)
            {
                var column = Table.Columns[i];
                if (!column.AllowDBNull && !column.IsValueType)
                {
                    if (!valueChecked)
                    {
                        response.AppendFormatLine(tabs, "foreach ({0} item in items)", Table.EOName);
                        response.AppendTabLine(tabs, "{");
                        valueChecked = true;
                    }
                    response.AppendFormatLine(tabs + 1, "if (item.{0} == null) throw new Exception(\"属性{0}不能为null.\");", column.CodeName.ToCoderPropertyName());
                }
            }

            if (valueChecked)
            {
                response.AppendTabLine(tabs, "}");
            }

            response.AppendLine();
            response.AppendTabLine(tabs, "int ret = 0;");

            string fieldList = string.Empty;
            string where = string.Empty;
            // set ...
            for (int i = 0; i < Table.Columns.Count; i++)
            {
                var column = Table.Columns[i];
                if (column.IsIdentity || (column.NativeType == "timestamp"))
                    continue;
                string paraName = DBTextStyle.GetParameterName(column.Name);
                fieldList += string.Format("{0}={1}, ", DBTextStyle.GetColumnName(column.Name), paraName);
            }
            fieldList = fieldList.Trim(',', ' ');

            //where 
            for (int i = 0; i < Table.PrimaryKey.MemberColumnNames.Count; i++)
            {
                string columnName = Table.PrimaryKey.MemberColumnNames[i];
                string paraName = GetOriginalParamName(columnName);

                if (i != 0)
                    where += " and ";
                where += string.Format("{0}={1}", DBTextStyle.GetColumnName(columnName), paraName);
            }

            response.AppendFormatLine(tabs, "const string sql = @\"update {0} set {1} where {2}\";", DBTextStyle.GetTableFullName(Table.Owner, Table.Name), fieldList, where);
            response.AppendLine();

            var sbPara = new StringBuilder();
            string para;
            GetParaInfo(tabs + 4, Table, true, out para);
            sbPara.Append(para);

            GetPrimayColumnParaInfo(tabs + 4, Table, true, out para);
            sbPara.Append(para);

            response.AppendFormatLine(tabs, "foreach ({0} item in items)", Table.EOName);
            response.AppendTabLine(tabs, "{");
            response.AppendTabLine(tabs + 1, "ret += Database.CreateSqlDao(sql)");
            response.Append(sbPara.ToString());
            response.AppendTabLine(tabs + 5, ".ExecuteNonQuery(tm);");
            response.AppendTabLine(tabs, "}");
            response.AppendTabLine(tabs, "return ret;");
        }
        #endregion
        #region 按字段更新
        /// <summary>
        /// 按字段更新数据
        /// </summary>
        /// <param name="response"></param>
        /// <param name="tabs"></param>
        private void ResponseUpdateColumns(StringBuilder response, int tabs)
        {
            if (!Table.HasPrimaryKey) return;

            for (int i = 0; i < Table.Columns.Count; i++)
            {
                var column = Table.Columns[i];

                if (column.IsPrimaryKey || column.IsIdentity || column.NativeType == "timestamp")
                    continue;
                ResponseUpdateColumn(response, column, tabs);
                response.AppendLine();
            }
        }

        private void ResponseUpdateColumn(StringBuilder response, CoderColumn updateColumn, int tabs)
        {
            string fieldList = string.Empty;
            string where = string.Empty;
            string primaryParam = ExtensionUtil.GetDataObjectsInfo(tabs, Table.PrimaryKey.MemberColumns, ExtensionUtil.IndexType.ParamList);
            string primarySummary = ExtensionUtil.GetDataObjectsInfo(tabs, Table.PrimaryKey.MemberColumns, ExtensionUtil.IndexType.ParamSummary);
            string columnParam = ExtensionUtil.GetDataObjectsInfo(tabs, new CoderColumn[] { updateColumn }, ExtensionUtil.IndexType.ParamList);
            string columnSummary = ExtensionUtil.GetDataObjectsInfo(tabs, new CoderColumn[] { updateColumn }, ExtensionUtil.IndexType.ParamSummary);


            string mainMethod = string.Format("public virtual int Update{0}({1}, {2}, TransactionManager tm = null)",
                                                updateColumn.CodeName,
                                                primaryParam,
                                                columnParam);

            var boMethodInfo = Table.MOInfo.Methods.Find(w => w.Method == mainMethod);

            if (boMethodInfo == null)
            {
                boMethodInfo = new CoderMOMethodInfo();

                boMethodInfo.CommandType = MOMethodCommandTypeEnum.Update;
                boMethodInfo.MethodType = MOMethodTypeEnum.Other;
                boMethodInfo.Method = mainMethod;
                boMethodInfo.IsBuild = true;
                Table.MOInfo.Methods.Add(boMethodInfo);
            }

            if (boMethodInfo.IsBuild)
            {
                response.AppendLine(ExtensionUtil.GetCoderSummary(tabs, "更新数据"));
                response.AppendLine(primarySummary);
                response.AppendLine(columnSummary);
                response.AppendLine(ExtensionUtil.GetCoderReturnSummary(tabs, string.Empty));
                response.AppendFormatLine(tabs, "public virtual int Update{0}({1}, {2})", updateColumn.CodeName, primaryParam, columnParam);
                response.AppendTabLine(tabs, "{");
                response.AppendFormatLine(tabs + 1,
                                           "return Update{0}({1}, {2}, null);",
                                           updateColumn.CodeName,
                                           ExtensionUtil.GetDataObjectsInfo(tabs, Table.PrimaryKey.MemberColumns, ExtensionUtil.IndexType.InputParam),
                                           ExtensionUtil.GetDataObjectsInfo(tabs, new CoderColumn[] { updateColumn }, ExtensionUtil.IndexType.InputParam));
                response.AppendTabLine(tabs, "}");
                response.AppendLine();
                response.AppendLine(ExtensionUtil.GetCoderSummary(tabs, "更新数据"));
                response.AppendLine(ExtensionUtil.GetCoderParamSummary(tabs, "tm", "事务管理对象"));
                response.AppendLine(primarySummary);
                response.AppendLine(columnSummary);
                response.AppendLine(ExtensionUtil.GetCoderReturnSummary(tabs, string.Empty));
                response.AppendTabLine(tabs, mainMethod);
                response.AppendTabLine(tabs, "{");

                if (!updateColumn.AllowDBNull && !updateColumn.IsValueType)
                {
                    response.AppendLine(ExtensionUtil.Tab(tabs + 1) + string.Format("if ({0} == null) throw new Exception(\"{0}不能为null.\");", updateColumn.CodeName.ToCoderParameterName()));
                }

                fieldList += string.Format("{0}={1} ", DBTextStyle.GetColumnName(updateColumn.Name), DBTextStyle.GetParameterName(updateColumn.Name));

                for (int i = 0; i < Table.PrimaryKey.MemberColumnNames.Count; i++)
                {
                    string columnName = Table.PrimaryKey.MemberColumnNames[i];
                    string paraName = DBTextStyle.GetParameterName(columnName);

                    where += string.Format("{0}={1}", DBTextStyle.GetColumnName(columnName), paraName) + ((i == (Table.PrimaryKey.MemberColumnNames.Count - 1)) ? string.Empty : " and ");
                }

                response.AppendFormatLine(tabs + 1, "const string sql = @\"UPDATE {0} SET {1} WHERE {2}\";", DBTextStyle.GetTableFullName(Table.Owner, Table.Name), fieldList, where);
                response.AppendTabLine(tabs + 1, "return AdoBase(sql)");
                string para;
                GetParaInfoForDelete(tabs + 5, new CoderColumn[] { updateColumn }.Concat(Table.PrimaryKey.MemberColumns), false, out para);
                response.Append(para);
                response.AppendTabLine(tabs + 5, ".ExecuteNonQuery(tm);");
                response.AppendTabLine(tabs, "}");
            }
        }
        #endregion

        #endregion

        #region Delete
        public class ResponseContent
        {
            public string Summary { get; set; }
            public string ParamDesc { get; set; }
            public string FunctionName { get; set; }
            public string ParamList { get; set; }
            public string InputParam { get; set; }
        }
        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="response"></param>
        /// <param name="tabs"></param>
        protected override void ResponseDelete(StringBuilder response, int tabs)
        {
            base.ResponseDelete(response, tabs);
            ResponseDeleteByPK(response, tabs);
            ResponseDeleteByFK(response, tabs);
            ResponseDeleteByUnique(response, tabs);
            ResponseDeleteByColumn(response, tabs);
            ResponseDeleteByWhereClause(response, tabs);
        }

        /// <summary>
        /// 按条件删除
        /// </summary>
        /// <param name="response"></param>
        /// <param name="tabs"></param>
        private void ResponseDeleteByWhereClause(StringBuilder response, int tabs)
        {
            const string mainMethod = "public virtual int Delete(string whereClause)";
            var boMethodInfo = Table.MOInfo.Methods.Find(w => w.Method == mainMethod);
            if (boMethodInfo == null)
            {
                boMethodInfo = new CoderMOMethodInfo();

                boMethodInfo.CommandType = MOMethodCommandTypeEnum.Delete;
                boMethodInfo.MethodType = MOMethodTypeEnum.Other;
                boMethodInfo.Method = mainMethod;
                boMethodInfo.IsBuild = true;
                Table.MOInfo.Methods.Add(boMethodInfo);
            }
            if (boMethodInfo.IsBuild)
            {
                response.AppendLine(ExtensionUtil.GetCoderSummary(tabs, "删除指定条件的记录"));
                response.AppendLine(ExtensionUtil.GetCoderParamSummary(tabs, "whereClause", "自定义删除条件，where子句。如：id=1 and name='aaa'"));
                response.AppendLine(ExtensionUtil.GetCoderParamSummary(tabs, "paras", "where子句中定义的参数集合"));
                response.AppendLine(ExtensionUtil.GetCoderTMSummary(tabs));
                response.AppendLine(ExtensionUtil.GetCoderReturnSummary(tabs, string.Empty));
                response.AppendTabLine(tabs, "public int Delete(string whereClause, IEnumerable<DbParameter> paras, TransactionManager tm = null)");
                response.AppendTabLine(tabs, "{");
                response.AppendFormatLine(tabs + 1, "var sql = string.Format(@\"DELETE FROM {0} {{0}}\", (string.IsNullOrEmpty(whereClause) ? string.Empty : \" WHERE \" + whereClause));"
                    , DBTextStyle.GetTableFullName(Table.Owner, Table.Name));
                response.AppendTabLine(tabs + 1, "return AdoBase(sql)");
                response.AppendTabLine(tabs + 5, ".AddParameters(paras)");
                response.AppendTabLine(tabs + 5, ".ExecuteNonQuery(tm);");
                response.AppendTabLine(tabs, "}");
            }
        }

        private void ResponseDeleteByPK(StringBuilder response, int tabs)
        {
            var content = new ResponseContent();
            foreach (var index in Table.Indexes.Where(index => index.IsPrimaryKey))
            {
                content.Summary = "按主键删除";
                content.ParamDesc = ExtensionUtil.GetDataObjectsInfo(tabs, index.MemberColumns, ExtensionUtil.IndexType.ParamSummary);
                content.FunctionName = "DeleteByPK";
                content.ParamList = ExtensionUtil.GetDataObjectsInfo(tabs, index.MemberColumns, ExtensionUtil.IndexType.ParamList);
                content.InputParam = ExtensionUtil.GetDataObjectsInfo(tabs, index.MemberColumns, ExtensionUtil.IndexType.InputParam);
                ResponseDeleteByColumns(response, tabs, index.MemberColumns, content, MOMethodTypeEnum.PK);
            }
        }

        private void ResponseDeleteByUnique(StringBuilder response, int tabs)
        {
            var content = new ResponseContent();
            foreach (var index in Table.Indexes)
            {
                if (index.IsUnique && !index.IsPrimaryKey)
                {
                    content.Summary = "按唯一键删除";
                    content.ParamDesc = ExtensionUtil.GetDataObjectsInfo(tabs, index.MemberColumns, ExtensionUtil.IndexType.ParamSummary);
                    content.FunctionName = "DeleteBy" + ExtensionUtil.GetDataObjectsInfo(tabs, index.MemberColumns, ExtensionUtil.IndexType.FunctionName);
                    content.ParamList = ExtensionUtil.GetDataObjectsInfo(tabs, index.MemberColumns, ExtensionUtil.IndexType.ParamList);
                    content.InputParam = ExtensionUtil.GetDataObjectsInfo(tabs, index.MemberColumns, ExtensionUtil.IndexType.InputParam);
                    ResponseDeleteByColumns(response, tabs, index.MemberColumns, content, MOMethodTypeEnum.UK);
                }
            }
        }

        private void ResponseDeleteByFK(StringBuilder response, int tabs)
        {
            foreach (var key in Table.ForeignKeys)
            {
                if (CheckIsOnlyFK(key)) continue;
                var content = new ResponseContent();
                content.Summary = "按外键删除";
                content.ParamDesc = ExtensionUtil.GetDataObjectsInfo(tabs, key.ForeignColumns, ExtensionUtil.IndexType.ParamSummary);
                content.FunctionName = "DeleteBy" + ExtensionUtil.GetDataObjectsInfo(tabs, key.ForeignColumns, ExtensionUtil.IndexType.FunctionName);
                content.ParamList = ExtensionUtil.GetDataObjectsInfo(tabs, key.ForeignColumns, ExtensionUtil.IndexType.ParamList);
                content.InputParam = ExtensionUtil.GetDataObjectsInfo(tabs, key.ForeignColumns, ExtensionUtil.IndexType.InputParam);
                ResponseDeleteByColumns(response, tabs, key.ForeignColumns, content, MOMethodTypeEnum.FK);
            }
        }
        /// <summary>
        /// 按字段删除
        /// </summary>
        /// <param name="response"></param>
        /// <param name="tabs"></param>
        private void ResponseDeleteByColumn(StringBuilder response, int tabs)
        {
            foreach (var column in Table.Columns)
            {
                if (column.IsUnique || column.IsForeignKey || column.IsPrimaryKey) continue;
                var columns = new List<CoderColumn>();
                columns.Add(column);
                var content = new ResponseContent();
                content.Summary = "按字段删除";
                content.ParamDesc = ExtensionUtil.GetDataObjectsInfo(tabs, columns, ExtensionUtil.IndexType.ParamSummary);
                content.FunctionName = "DeleteBy" + ExtensionUtil.GetDataObjectsInfo(tabs, columns, ExtensionUtil.IndexType.FunctionName);
                content.ParamList = ExtensionUtil.GetDataObjectsInfo(tabs, columns, ExtensionUtil.IndexType.ParamList);
                content.InputParam = ExtensionUtil.GetDataObjectsInfo(tabs, columns, ExtensionUtil.IndexType.InputParam);
                ResponseDeleteByColumns(response, tabs, columns, content, MOMethodTypeEnum.Other);
            }
        }

        private void ResponseDeleteByColumns(StringBuilder response, int tabs, IEnumerable<CoderColumn> columns, ResponseContent content, MOMethodTypeEnum methodType)
        {
            string mainMethod = string.Format("public int {0}({1}, TransactionManager tm = null)",
                content.FunctionName,
                content.ParamList);
            var boMethodInfo = Table.MOInfo.Methods.Find(w => w.Method == mainMethod);
            if (boMethodInfo == null)
            {
                boMethodInfo = new CoderMOMethodInfo();
                boMethodInfo.CommandType = MOMethodCommandTypeEnum.Delete;
                boMethodInfo.MethodType = methodType;
                boMethodInfo.Method = mainMethod;
                boMethodInfo.IsBuild = true;
                Table.MOInfo.Methods.Add(boMethodInfo);
            }

            if (boMethodInfo.IsBuild)
            {
                response.AppendLine(ExtensionUtil.GetCoderSummary(tabs, content.Summary));
                response.AppendLine(ExtensionUtil.GetCoderTMSummary(tabs));
                response.AppendLine(content.ParamDesc);
                response.AppendTabLine(tabs, mainMethod);
                response.AppendTabLine(tabs, "{");

                var where = string.Empty;
                foreach (var column in columns)
                {
                    var paraName = DBTextStyle.GetParameterName(column.Name);
                    where += string.Format("{0}={1}", DBTextStyle.GetColumnName(column.Name), paraName) + " AND ";
                }
                where = where.TrimString(null, " AND ");
                response.AppendFormatLine(tabs + 1, "const string sql = @\"DELETE FROM {0} WHERE {1}\";",
                    DBTextStyle.GetTableFullName(Table.Owner, Table.Name),
                    where);
                response.AppendTabLine(tabs + 1, "return AdoBase(sql)");
                string para;
                GetParaInfoForDelete(tabs + 5, columns, false, out para);
                response.Append(para);
                response.AppendTabLine(tabs + 5, ".ExecuteNonQuery(tm);");
                response.AppendTabLine(tabs, "}");
            }
        }

        #endregion

        #region Utils

        /// <summary>
        /// 获得原始主键Sql参数名
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public virtual string GetOriginalParamName(string name)
        {
            return DBTextStyle.GetParameterName(name.ToCoderPropertyNameByPrimaryColumn());
        }

        private void GetParaInfo(int tabs, CoderTable sourceTable, bool paramValueNeedIndent, out string para)
        {
            GetParaInfo(tabs, sourceTable.Columns, paramValueNeedIndent, out para);
        }

        private void GetParaInfo(int tabs, IEnumerable<CoderColumn> columns, bool paramValueNeedIndent, out string para)
        {
            var sbPara = new StringBuilder();
            int paramValueNeedIndentTab = tabs + (paramValueNeedIndent ? 1 : 0);
            tabs = tabs + (paramValueNeedIndent ? 1 : 0);

            foreach (var column in columns)
            {
                if (column.IsIdentity || (column.NativeType == "timestamp"))
                    continue;

                Load(sbPara, column, tabs, paramValueNeedIndentTab, GetColumnParaNameString, GetColumnParaValueString);
            }
            para = sbPara.ToString();
        }

        private void GetParaInfoForDelete(int tabs, IEnumerable<CoderColumn> columns, bool paramValueNeedIndent, out string para)
        {
            var sbPara = new StringBuilder();
            var paramValueNeedIndentTab = tabs + (paramValueNeedIndent ? 1 : 0);
            tabs = tabs + (paramValueNeedIndent ? 1 : 0);
            foreach (var column in columns)
                Load(sbPara, column, tabs, paramValueNeedIndentTab, GetColumnParaNameString, GetColumnParaValueStringForDelete);
            para = sbPara.ToString();
        }


        private void GetPrimayColumnParaInfo(int tabs, CoderTable sourceTable, bool paramValueNeedIndent, out string para)
        {
            var sbPara = new StringBuilder();
            var paramValueNeedIndentTab = tabs + (paramValueNeedIndent ? 1 : 0);
            tabs = tabs + (paramValueNeedIndent ? 1 : 0);
            foreach (var column in sourceTable.PrimaryKey.MemberColumns)
            {
                Load(sbPara, column, tabs, paramValueNeedIndentTab, GetSqlParaNameForPrimayColumn, GetValueForPrimayColumn);
            }
            para = sbPara.ToString();
        }

        private static void Load(StringBuilder para, CoderColumn column, int tabs, int paramValueNeedIndentTab, Func<CoderColumn, string> getSqlParaNameFun, Func<CoderColumn, string> getParaValueFun)
        {
            para.AppendFormatLine(tabs,
                                    ".AddInParameter(\"{0}\", {1}, DbType.{2}, {3})",
                                    getSqlParaNameFun(column),
                                    getParaValueFun(column),
                                    column.DbType,
                                    column.NativeType == "text" || column.NativeType == "ntext" ? -1 : column.Size);
        }

        private string GetColumnParaNameString(CoderColumn column)
        {
            return DBTextStyle.GetParameterName(column.Name);
        }

        private static string GetColumnParaValueString(CoderColumn column)
        {
            if (!column.AllowDBNull)
                return string.Format("item.{0}", column.CodeName.ToCoderPropertyName());

            if (column.IsValueType)
                return string.Format("item.{0}.HasValue ? (object)item.{0}.Value : DBNull.Value", column.CodeName.ToCoderPropertyName());
            else
                return string.Format("item.{0} != null ?  (object)item.{0} : DBNull.Value", column.CodeName.ToCoderPropertyName());

        }

        public static string GetColumnParaValueString(string columnName, CoderDataObject dataObjectBase)
        {
            if (!dataObjectBase.AllowDBNull)
                return string.Format("{0}", columnName);

            if (dataObjectBase.IsValueType)
                return string.Format("{0}.HasValue ? (object){0}.Value : DBNull.Value", columnName);
            else
                return string.Format("{0} != null ?  (object){0} : DBNull.Value", columnName);
        }

        private static string GetColumnParaValueStringForDelete(CoderColumn column)
        {
            return GetColumnParaValueString(column.CodeName.ToCoderParameterName(), column);
        }


        private string GetSqlParaNameForPrimayColumn(CoderColumn column)
        {
            return GetOriginalParamName(column.CodeName);
        }

        private static string GetValueForPrimayColumn(CoderColumn column)
        {
            return string.Format("(item.HasOriginal) ? item.{0} : item.{1}", column.CodeName.ToCoderPropertyNameByPrimaryColumn(), column.CodeName.ToCoderPropertyName());
        }
        #endregion

    }
}
